package org.dromara.quick.chat.controller;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.encrypt.properties.EncryptorProperties;
import org.dromara.common.encrypt.utils.AESUtil;
import org.dromara.common.encrypt.utils.EncryptUtils;
import org.dromara.common.model.enums.channel.ChannelTypeEnum;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.wukong.service.ChannelService;
import org.dromara.common.wukong.service.WuKongConversationService;
import org.dromara.quick.chat.domain.ImChatConversation;
import org.dromara.quick.chat.model.bo.ImChatConversationReadMaxSeqBO;
import org.dromara.quick.chat.model.bo.query.SyncChannelOfflineMessageBo;
import org.dromara.quick.chat.service.ImChatConversationService;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 消息同步
 * @Author: Stars
 * @description 针对表【im_message(好友消息表-使用MongoDB库[不能有失效时间，因为消息是对方看到后多少秒销毁，并非发送后多少秒销毁])】的数据库操作Service
 * @Date: 2025/3/6 22:32
 */
@Slf4j
@RequiredArgsConstructor
@RestController
@RequestMapping("/im/ban/record")
public class ImMessageController extends BaseController {


    private final ChannelService channelService;

    private final WuKongConversationService wuKongConversationService;

    private final ImChatConversationService imChatConversationService;

    private final EncryptorProperties encryptorProperties;

    /**
     * 同步最近会话
     * <p>
     *     也就是刚进入app时首页的会话列表，同步会话时同时会查询近20条消息
     * </p>
     */
    @PostMapping("/sync/chat/conversation")
    public R<?> syncChatConversation() {
        Long userId = LoginHelper.getUserId();
        try {
            JSONArray jsonArray1 = wuKongConversationService.syncConversation(userId);
            if (jsonArray1.isEmpty()) {
                return R.ok("OK", jsonArray1);
            }

            // 使用并行流处理（根据实际CPU核心数调整）
            jsonArray1.parallelStream().forEach(body -> {
                JSONObject conversation = (JSONObject) body;
                JSONArray messagesArray = conversation.getJSONArray("recents");

                // 直接在原数组上过滤无效消息
                if (CollectionUtils.isNotEmpty(messagesArray)) {
                    JSONObject message = (JSONObject) messagesArray.get(0);
                    String payload = message.getString("payload");

                    // 先解密并检查是否需要保留此消息
                    String msgJson = EncryptUtils.decryptByBase64(payload);
                    JSONObject rootObject = JSON.parseObject(msgJson);
                    // 解析最外层JSON
                    String contentJson = rootObject.getString("content");
                    // 解密消息

                    // 关键优化点：根据业务规则直接判断是否保留消息
                    try {
                        JSONObject jsonObject = JSON.parseObject(AESUtil.decrypt(contentJson, encryptorProperties.getChatKey()));
                        if (shouldKeepMessage(jsonObject)) {
                            // 替换原始消息数组为过滤后的数组
                            conversation.put("recents", jsonObject);
                        }
                    } catch (Exception e) {
                        log.info("消息解密错误：消息：{}",contentJson);
                        e.printStackTrace();
                    }
                }
            });
            // 加密传输
            String encryptByAes = EncryptUtils.encryptByAes(jsonArray1.toString(), encryptorProperties.getAppKey());
            return R.ok("OK", encryptByAes);
        } catch (Exception e) {
            // 统一异常处理，添加日志记录
            log.error("同步会话失败: {}", e.getMessage(), e);
            return R.fail("同步会话失败", e.getMessage());
        }
    }

    /**
     * 根据业务规则判断消息是否应该保留
     * @param message
     * @return
     */
    private boolean shouldKeepMessage(JSONObject message) {
        // 示例：检查消息类型、时间戳或其他业务字段
        // 这里仅为示例，实际规则需根据业务需求调整
        if (message == null) return false;

        // 检查是否为阅后即焚消息
//        boolean isBurnAfterReading = message.getBooleanValue("burnAfterReading");
//        if (isBurnAfterReading) {
//            // 可以进一步检查阅读状态
//            return false;
//        }

        // 检查是否已删除
//        boolean isDeleted = message.getBooleanValue("deleted");
//        return !isDeleted;

        return true;
    }

    /**
     * 设置未读消息为 0---- app段点击删除聊天记录
     * <p>
     *     主要在用户退出聊天框时调用
     * </p>
     */
    @PostMapping("/set/unread/message")
    public R<?> setUnreadMessage(@RequestBody SyncChannelOfflineMessageBo syncChannelOfflineMessageBo) throws IOException {
        if (syncChannelOfflineMessageBo.getReceiverId() == null){
            return R.fail("参数错误，消息同步失败");
        }
        Long userId = LoginHelper.getUserId();
        wuKongConversationService.setConversationUnreadCount(userId, syncChannelOfflineMessageBo.getReceiverId().toString(), syncChannelOfflineMessageBo.getChannelType());
        // 设置消息最大id
        imChatConversationService.updateReadMaxSeq(ImChatConversationReadMaxSeqBO.builder()
            .senderId(userId)
            .receiverId(syncChannelOfflineMessageBo.getReceiverId())
            .channelType(syncChannelOfflineMessageBo.getChannelType())
            .messageSeq(syncChannelOfflineMessageBo.getMessageSeq()).build());
        return R.ok();
    }

    /**
     * 同步 用户 最近会话信息
     * <p>
     *     获取消息的最大id，不管是删除的还是已经查询到的。例如：app本地 最大消息id=10，删除消息id=8以前的数据。那么查询消息时从消息id > 10开始查询走。如果服务器存储的消息id=20，此时消息也从20查询起走。因为服务器的消息id都是app端查询到的消息最大id拿过来更改的。
     *
     *     用户与用户的聊天记录是查询全部
     * </p>
     * @param syncChannelOfflineMessageBo
     * @return
     */
    @PostMapping("/sync/channel/offline/user")
    public R<?> syncChannelOfflineMessage(@RequestBody SyncChannelOfflineMessageBo syncChannelOfflineMessageBo) throws IOException {
        if (syncChannelOfflineMessageBo.getReceiverId() == null){
            return R.fail("参数错误，消息同步失败");
        }
        Long userId = LoginHelper.getUserId();
        Long maxMessageSeq =0L;
        // 查询会话信息
        ImChatConversation imChatConversation = imChatConversationService.selectByCondition(userId.toString(), syncChannelOfflineMessageBo.getReceiverId().toString(), ChannelTypeEnum.PERSONAL_CHANNEL.getCode());
        if (imChatConversation != null) {
            maxMessageSeq = StreamUtils.max(imChatConversation.getMessageSeq(), imChatConversation.getDeleteMessageSeq());
        }
        if (maxMessageSeq == null){
            maxMessageSeq = 0L;
        }
        JSONArray jsonArray = channelService.syncOfflineMessage(userId.toString(), syncChannelOfflineMessageBo.getReceiverId().toString(), maxMessageSeq, 30L);
        // 设置会话未读数量
        wuKongConversationService.setConversationUnreadCount(userId, syncChannelOfflineMessageBo.getReceiverId().toString(), ChannelTypeEnum.PERSONAL_CHANNEL.getCode());
        if (CollectionUtils.isEmpty(jsonArray)){
            return R.ok("OK", jsonArray);
        }
        List<String> msgList = jsonArray.parallelStream()
            .map(body -> {
                JSONObject conversation = (JSONObject) body;
                String msg = conversation.getString("payload");
                Integer messageSeq = conversation.getInteger("message_seq");
                // 设置消息最大id
                imChatConversationService.updateReadMaxSeq(ImChatConversationReadMaxSeqBO.builder()
                    .senderId(userId)
                    .receiverId(syncChannelOfflineMessageBo.getReceiverId())
                    .channelType(syncChannelOfflineMessageBo.getChannelType())
                    .messageSeq(syncChannelOfflineMessageBo.getMessageSeq()).build());
                // 处理空消息，返回null或空字符串，取决于业务需求
                if (StringUtils.isBlank(msg)) {
                    return null;
                }
                // 解密传输，消息解密：先用base64解码，在解密
                try {
                    String byBase64 = EncryptUtils.decryptByBase64(msg);
                    JSONObject jsonObject = JSONObject.parseObject(byBase64);
                    String decrypt = AESUtil.decrypt(jsonObject.getString("content"), encryptorProperties.getChatKey());
                    JSONObject jsonObj = JSONObject.parseObject(decrypt);
                    jsonObj.put("messageSeq",messageSeq);
                    return jsonObj.toJSONString();
                } catch (Exception e) {
                    // 记录异常信息
                    System.err.println("解密失败: " + e.getMessage());
                    return null;
                }
            })
            .filter(Objects::nonNull)
            .collect(Collectors.toList());
        // 加密传输
        String encryptByAes = EncryptUtils.encryptByAes(msgList.toString(), encryptorProperties.getAppKey());
        return R.ok("OK", encryptByAes);
    }

    /**
     * 同步最近群组消息，app刚进入聊天框时查询的第一页数据
     * <p>
     *   群组的消息只查询30条，app端往上滑动时在从服务器查询。注意：群组的消息记录都是从服务器端查询，而用户与用户的聊天记录都是从app本地查询，但是进入聊天页面时会同步离线消息。
     * </p>
     * @param syncChannelOfflineMessageBo
     * @return
     * @see  ImMessageController#syncChannelOfflineMessage(SyncChannelOfflineMessageBo) 消息id说明参考
     */
    @PostMapping("/sync/channel/offline/group/recently")
    public R<?> syncOfflineGroupRecentlyMessage(@RequestBody SyncChannelOfflineMessageBo syncChannelOfflineMessageBo) throws IOException {
        if (syncChannelOfflineMessageBo.getReceiverId() == null){
            return R.fail("参数错误，消息同步失败");
        }
        Long userId = LoginHelper.getUserId();
        Long maxMessageSeq =0L;
        // 查询会话信息
        ImChatConversation imChatConversation = imChatConversationService.selectByCondition(userId.toString(), syncChannelOfflineMessageBo.getReceiverId().toString(), ChannelTypeEnum.GROUP_CHANNEL.getCode());
        if (imChatConversation != null && imChatConversation.getDeleteMessageSeq() != null){
            maxMessageSeq =imChatConversation.getDeleteMessageSeq();
        }
        JSONArray jsonArray = channelService.syncOfflineGroupRecentlyMessage(userId.toString(), syncChannelOfflineMessageBo.getReceiverId().toString(), maxMessageSeq,50L);
        // 将该频道的已读消息设置成0，因为进入当前聊天页面，消息已经全部看到了。
        wuKongConversationService.setConversationUnreadCount(userId, syncChannelOfflineMessageBo.getReceiverId().toString(), ChannelTypeEnum.GROUP_CHANNEL.getCode());
        if (CollectionUtils.isEmpty(jsonArray)){
            return R.ok("OK", jsonArray);
        }

        List<String> msgList = jsonArray.stream()
            .map(body -> {
                JSONObject conversation = (JSONObject) body;
                String msg = conversation.getString("payload");
                Integer messageSeq = conversation.getInteger("message_seq");

                // 处理空消息，返回null或空字符串，取决于业务需求
                if (StringUtils.isBlank(msg)) {
                    return null;
                }
                // 解密传输，消息解密：先用base64解码，在解密
                try {
                    String byBase64 = EncryptUtils.decryptByBase64(msg);
                    JSONObject jsonObject = JSONObject.parseObject(byBase64);
                    String decrypt = AESUtil.decrypt(jsonObject.getString("content"), encryptorProperties.getChatKey());
                    JSONObject jsonObj = JSONObject.parseObject(decrypt);
                    jsonObj.put("messageSeq",messageSeq);
                    return jsonObj.toJSONString();
                } catch (Exception e) {
                    // 记录异常信息
                    System.err.println("解密失败: " + e.getMessage());
                    return null;
                }
            })
            .filter(Objects::nonNull)
            .collect(Collectors.toList());
        // 加密传输
        String encryptByAes = EncryptUtils.encryptByAes(msgList.toString(), encryptorProperties.getAppKey());
        return R.ok("OK", encryptByAes);
    }

    /**
     * 同步 群组 历史消息，app进入聊天页面往上滑动时查询的历史消息
     */
    @PostMapping("/sync/group/history/message")
    public R<?> syncGroupHistoryMessage(@RequestBody SyncChannelOfflineMessageBo syncChannelOfflineMessageBo) throws IOException {
        if (syncChannelOfflineMessageBo.getReceiverId() == null || syncChannelOfflineMessageBo.getMessageSeq() == null){
            return R.fail("参数错误，消息同步失败");
        }
        if (syncChannelOfflineMessageBo.getMessageSeq() != null && syncChannelOfflineMessageBo.getMessageSeq() <= 1){
            // 因为messageSeq开始值就是1
            return R.ok();
        }
        Long userId = LoginHelper.getUserId();
        Long deleteMessageSeq =0L;
        // 查询会话信息
        ImChatConversation imChatConversation = imChatConversationService.selectByCondition(userId.toString(), syncChannelOfflineMessageBo.getReceiverId().toString(), ChannelTypeEnum.GROUP_CHANNEL.getCode());
        if (imChatConversation != null && imChatConversation.getDeleteMessageSeq() != null){
            deleteMessageSeq =imChatConversation.getDeleteMessageSeq();
        }
        // 删除的消息id一定要小于开始查询的消息id，详细参考官方文档，pull_mode为0 表示向下拉，逻辑如下：
        if (deleteMessageSeq >= syncChannelOfflineMessageBo.getMessageSeq()){
            return R.ok();
        }
        JSONArray jsonArray = channelService.syncGroupHistoryMessage(userId.toString(), syncChannelOfflineMessageBo.getReceiverId().toString(), syncChannelOfflineMessageBo.getMessageSeq(),deleteMessageSeq, 50L);
        List<String> msgList = jsonArray.parallelStream()
            .map(body -> {
                JSONObject conversation = (JSONObject) body;
                String msg = conversation.getString("payload");
                Integer messageSeq = conversation.getInteger("message_seq");
                // 设置消息最大id
                imChatConversationService.updateReadMaxSeq(ImChatConversationReadMaxSeqBO.builder()
                    .senderId(userId)
                    .receiverId(syncChannelOfflineMessageBo.getReceiverId())
                    .channelType(syncChannelOfflineMessageBo.getChannelType())
                    .messageSeq(syncChannelOfflineMessageBo.getMessageSeq()).build());

                // 处理空消息，返回null或空字符串，取决于业务需求
                if (StringUtils.isBlank(msg)) {
                    return null;
                }
                // 解密传输，消息解密：先用base64解码，在解密
                try {
                    String byBase64 = EncryptUtils.decryptByBase64(msg);
                    JSONObject jsonObject = JSONObject.parseObject(byBase64);
                    String decrypt = AESUtil.decrypt(jsonObject.getString("content"), encryptorProperties.getChatKey());
                    JSONObject jsonObj = JSONObject.parseObject(decrypt);
                    jsonObj.put("messageSeq",messageSeq);
                    return jsonObj.toJSONString();
                } catch (Exception e) {
                    // 记录异常信息
                    System.err.println("解密失败: " + e.getMessage());
                    return null;
                }
            })
            .filter(Objects::nonNull)
            .collect(Collectors.toList());
        // 加密传输
        String encryptByAes = EncryptUtils.encryptByAes(msgList.toString(), encryptorProperties.getAppKey());
        return R.ok("OK", encryptByAes);
    }

}
